package com.zzw.okhttp3;

import okhttp3.Call;
import okhttp3.FormBody;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okio.BufferedSink;
import org.jetbrains.annotations.NotNull;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.Objects;

/**
 * @author Administrator
 */
public class DefaultOkhttp3 implements Okhttp3 {

    @Override
    public String postRequest(OkHttpClient httpClient, RequestBody requestBody, String url, Map<String, String> headers) throws IOException {
        Call call = makeRequestCall(httpClient, requestBody, url, headers);
        return postRequest(call);
    }

    public String buildUrlIncludeQueryParams(String url, Map<String, String> queryParams) {
        HttpUrl.Builder builder = Objects.requireNonNull(HttpUrl.parse(url)).newBuilder();
        queryParams.forEach(builder::addQueryParameter);
        return builder.build().toString();
    }

    public RequestBody buildJsonRequestBody(String json) {
        return RequestBody.create(json, MediaType.parse("application/json; charset=utf-8"));
    }

    public RequestBody buildFormRequestBody(Map<String, String> formBody) {
        FormBody.Builder builder = new FormBody.Builder();
        formBody.forEach(builder::add);
        return builder.build();
    }

    public RequestBody buildMultipartRequestBodyOfFiles(Map<String, String> formBody, Map<String, FilePat> fileMap) {
        MultipartBody.Builder builder = new MultipartBody.Builder();
        builder.setType(MultipartBody.FORM);
        formBody.forEach(builder::addFormDataPart);
        fileMap.forEach((k, v) -> builder.addFormDataPart(k, v.getFile().getName(), RequestBody.create(v.getFile(), v.getMediaType())));
        return builder.build();
    }

    public RequestBody buildMultipartFilesRequestBody(Map<String, String> formBody, Map<String, MultipartFile> fileMap) {
        MultipartBody.Builder builder = new MultipartBody.Builder();
        builder.setType(MultipartBody.FORM);
        formBody.forEach(builder::addFormDataPart);
        fileMap.forEach((k, v) -> {
            try {
                builder.addFormDataPart(k, v.getName(), RequestBody.create(v.getBytes(), MediaType.get(Objects.requireNonNull(v.getContentType()))));
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        });
        return builder.build();
    }

    public RequestBody buildMultipartFilesInputStreamRequestBody(Map<String, String> formBody, Map<String, MultipartFile> fileMap) {
        MultipartBody.Builder builder = new MultipartBody.Builder();
        builder.setType(MultipartBody.FORM);
        formBody.forEach(builder::addFormDataPart);
        fileMap.forEach((k, v) -> {
            builder.addFormDataPart(k, v.getOriginalFilename(), new RequestBody() {
                @NotNull
                @Override
                public MediaType contentType() {
                    return MediaType.get(Objects.requireNonNull(v.getContentType()));
                }

                @Override
                public long contentLength() {
                    return v.getSize();
                }

                @Override
                public void writeTo(@NotNull BufferedSink bufferedSink) throws IOException {
                    try (InputStream inputStream = v.getInputStream()) {
                        byte[] buffer = new byte[2048];
                        int len;
                        while ((len = inputStream.read(buffer)) != -1) {
                            bufferedSink.write(buffer, 0, len);
                            bufferedSink.flush();
                        }
                    }
                }
            });
        });
        return builder.build();
    }

    public static class FilePat {
        private File file;
        private MediaType mediaType;

        public File getFile() {
            return file;
        }

        public void setFile(File file) {
            this.file = file;
        }

        public MediaType getMediaType() {
            return mediaType;
        }

        public void setMediaType(MediaType mediaType) {
            this.mediaType = mediaType;
        }
    }


    private Call makeRequestCall(OkHttpClient httpClient, RequestBody body, String reqUrl, Map<String, String> headers) {
        Request.Builder reqBuilder = new Request.Builder();
        reqBuilder.url(reqUrl).post(body);
        if (headers != null && !headers.isEmpty()) {
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                reqBuilder.header(entry.getKey(), entry.getValue());
            }
        }
        return httpClient.newCall(reqBuilder.build());
    }

    private String postRequest(Call call) throws IOException {
        try (Response resp = call.execute()) {
            ResponseBody rb = resp.body();
            if (resp.code() != 200) {
                String msg = null;
                if (rb != null) {
                    msg = rb.string();
                }
                throw new IOException("http code: " + resp.code() + (msg != null ? ",msg:" + msg : ""));
            }
            if (rb == null) {
                throw new IllegalStateException("http response body is null");
            }
            return rb.string();
        }
    }

}
